package core

import (
	"context"
	"fmt"
	"ntrace/utils/routines"
	"sort"
	"strings"
	"time"
)

type Printer struct {
	Ctx  context.Context
	Task *TraceTask
	Stat *TraceStat
}

func NewPrinter(ctx context.Context, task *TraceTask, stat *TraceStat) *Printer {
	return &Printer{
		Ctx:  ctx,
		Task: task,
		Stat: stat,
	}
}

func (p *Printer) StartPrint() error {
	routines.GoWithRecover("Printer", p.doPrint)
	return nil
}

func (p *Printer) doPrint() {
	initTimer := time.NewTimer(1500 * time.Millisecond)
	defer initTimer.Stop()
	select {
	case <-p.Ctx.Done():
		time.Sleep(500 * time.Millisecond)
		p.printStat()
		return
	case <-initTimer.C:
		p.printStat()
	}

	ticker := time.NewTicker(5 * time.Second)
	defer ticker.Stop()
	for {
		select {
		case <-p.Ctx.Done():
			time.Sleep(500 * time.Millisecond)
			p.printStat()
			return
		case <-ticker.C:
			p.printStat()
		}
	}
}

func (p *Printer) printStat() {

	fmt.Println("=====================================")
	p.printHopStat()

	fmt.Println()
	fmt.Println("=====================================")
	fmt.Println("abnormal flows:")
	p.printAbnormalFlow()
}

func (p *Printer) printHopStat() {
	var index int
	for index = len(p.Stat.TraceHopStats) - 1; index > 0; index-- {
		if p.Stat.TraceHopStats[index] != nil {
			break
		}
	}

	fmt.Printf("%s %-15s %-8s %-8s %-8s %-8s %-8s %-8s\n",
		"ttl", "target", "total", "loss", "success", "min(ms)", "avg(ms)", "max(ms)")

	for ttl := 1; ttl <= index; ttl++ {
		hop := p.Stat.TraceHopStats[ttl]
		if hop == nil {
			fmt.Printf("%-3d %-15s %-8d %-8d %-8s %-8.2f %-8.2f %-8.2f\n", ttl, "?", 0, 0, "0%", 0.0, 0.0, 0.0)
			continue
		}

		var devices []string
		for device := range hop.DeviceSet {
			devices = append(devices, device)
		}

		sort.Strings(devices)
		if len(devices) > 1 && strings.Contains(devices[0], "*") {
			first := devices[0]
			devices = devices[1:]
			devices = append(devices, first)
		}

		for i, device := range devices {
			if i == 0 {
				minLat := float64(hop.MinLatency) / float64(time.Millisecond)
				avgLat := float64(hop.AvgLatency) / float64(time.Millisecond)
				maxLat := float64(hop.MaxLatency) / float64(time.Millisecond)
				var rateStr string
				if hop.Loss == 0 {
					rateStr = "100%"
				} else if hop.Loss == hop.Total {
					rateStr = "0%"
				} else {
					rate := (1 - float64(hop.Loss)/float64(hop.Total)) * 100
					rateStr = fmt.Sprintf("%.4f%%", rate)
				}

				fmt.Printf("%-3d %-15s %-8d %-8d %-8s %-8.2f %-8.2f %-8.2f\n",
					ttl, device, hop.Total, hop.Loss, rateStr, minLat, avgLat, maxLat)
			} else {
				fmt.Printf("    %-15s\n", device)
			}
		}
	}
}

func (p *Printer) printAbnormalFlow() {
	var keys []string
	for key := range p.Stat.AbnormalFlowRecordMap {
		keys = append(keys, key)
	}

	sort.Strings(keys)

	for _, key := range keys {
		textSet := make(map[string]bool)
		abnormals := p.Stat.AbnormalFlowRecordMap[key]
		fmt.Println(key + " :")
		for _, abnormal := range abnormals {
			if !textSet[abnormal.Text] {
				textSet[abnormal.Text] = true
				fmt.Println("    " + abnormal.Text)
			}
		}
	}
}
